package com.xiashitech.agent.grpc;

import com.xiashitech.agent.config.StartConfig;
import com.xiashitech.agent.dns.CustomDns;
import com.xiashitech.agent.dns.GrpcResolverProvider;
import io.grpc.*;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2SecurityUtil;
import io.grpc.netty.shaded.io.netty.handler.ssl.*;
import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;

import java.net.InetAddress;
import java.util.Random;

public class XsChannelManager {
    private final static XsChannelManager manager = new XsChannelManager();
    private ManagedChannel channel;
    private Object lockobj = new Object();

    public static XsChannelManager getManager() {
        return manager;
    }

    public ManagedChannel buildStubOfChannel(String host, int serverPort) {
        System.out.println(Thread.currentThread().getId() + ", buildStubOfChannel enter ........");
        synchronized (lockobj) {
            if(channel == null) {
                // 按当前配置漂移 10 的方式启动连 rpc 端口
                if(StartConfig.getGrpcPort() == 0) {
                    String url = StartConfig.getParamValueFromKey(StartConfig.agentService);
                    if (url != null && !"".equals(url)) {
                        String[] urlArr = url.split(":");
                        if (urlArr.length == 2) {
                            host = urlArr[0];
                            serverPort = Integer.parseInt(urlArr[1]) + 10;
                        }
                    }
                } else {
                    host = StartConfig.getGrpcServer();
                    serverPort = StartConfig.getGrpcPort();
                }
                if(StartConfig.isSecureGrpc()) {
                    try {
                        String target = host+":"+serverPort;
                        channel = NettyChannelBuilder.forTarget(target)
                                .sslContext(SslContextBuilder.forClient()
                                        .sslProvider(OpenSsl.isAlpnSupported() ? SslProvider.OPENSSL : SslProvider.JDK)
                                        .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
                                        .trustManager(InsecureTrustManagerFactory.INSTANCE)
                                        .applicationProtocolConfig(new ApplicationProtocolConfig(
                                                ApplicationProtocolConfig.Protocol.ALPN,
                                                ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
                                                ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
                                                ApplicationProtocolNames.HTTP_2,
                                                ApplicationProtocolNames.HTTP_1_1))
                                        .build()
                                ).build();
                        System.out.println(Thread.currentThread().getId() + ",Security SSL-HTTP2  channel build, target: " + target);
                    } catch (Exception e) {
                        StartConfig.throwableStackPrinter(e);
                    }
                } else {

                    System.out.println(Thread.currentThread().getId() + ", channel build ...............: " + host + ":" + serverPort);
                    String nameserver = StartConfig.getParamValueFromKey(StartConfig.nameServer); // Config 中取
                    if (null == nameserver || "".equals(nameserver)) {
                        channel = ManagedChannelBuilder.forAddress(host, serverPort) //
                                //.defaultLoadBalancingPolicy("custom_round_robin")
                                .usePlaintext() //无需加密或认证
                                .build();
                    } else {
                        if ("customrandom".equals(StartConfig.getParamValueFromKey("lbmode"))) {
                            // 以下方式也可以，每次启动后，就固定了，条件：也是要配了 nameserver
                            System.out.println("channel build ........ customrand nameserver: " + nameserver + " host: " + host);
                            InetAddress[] addresses = CustomDns.getInstance().getDnsRealIps(host);
                            if (addresses == null || addresses.length == 0) {
                                System.out.println("channel build ........ " + host + " dns custom resolve failed." + addresses + " maybe CoreDNS config error or CoreDNS failed");
                                return null;
                            }
                            InetAddress address = addresses[0];
                            if (addresses.length > 1) {
                                Random r = new Random();
                                int n = r.nextInt(addresses.length);
                                address = addresses[n];
                            }
                            System.out.println("channel build ........ customrand use host: " + address.getHostAddress() + " port: " + serverPort);
                            channel = ManagedChannelBuilder.forAddress(address.getHostAddress(), serverPort)
                                    .usePlaintext()
                                    .build();
                        } else {
                            NameResolverRegistry.getDefaultRegistry().register(new GrpcResolverProvider());
                            channel = ManagedChannelBuilder.forTarget("xiashi://" + host + ":" + serverPort)
                                    .usePlaintext()
                                    .build();
                        }
                    }
                }
                System.out.println(Thread.currentThread().getId() + ", channel build end ........");
            }
            System.out.println(Thread.currentThread().getId() + ", buildStubOfChannel initChannel  out........identityHashCode: " + System.identityHashCode(channel));
            return channel;
        }
    }
}
